/*
    该程序用于演示 Linux基于TCP的文件传输  的服务器端
    服务器流程: (接收文件) 
    1)接收文件名和文件大小
    2)给客户端回复确认报文,表示客户端可以发送文件了
    3)接收文件内容
    4)给客户端回复确认报文,表示文件已经接收成功
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <string>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

using namespace std;

struct st_fileinfo{
    char filename[256];  //文件名
    long filesize;               //文件大小
};

int main(int argc,char* argv[]){
    
    if(argc != 3){
        printf("using: ./server ip port\n");
        printf("example: ./server 127.0.0.1 55554\n");
        return -1;
    }

    //创建监听套接字
    int lfd = socket(AF_INET,SOCK_STREAM,0);
    if(lfd == -1){
        perror("lfd create failed.");
        return -1;
    }
    //定义网络地址结构
    struct sockaddr_in srv_addr;
    memset(&srv_addr,0,sizeof(struct sockaddr_in));
    srv_addr.sin_family = AF_INET;   //ipv4
    srv_addr.sin_port = htons(atoi(argv[2]));
    srv_addr.sin_addr.s_addr = htonl(INADDR_ANY);

    //套接字绑定网络地址结构
    if(bind(lfd,(struct sockaddr*)&srv_addr,sizeof(srv_addr)) == -1){
        perror("bind() failed.");
        return -1;
    }

    //设置最大监听队列
    listen(lfd,32);

    //定义连接的客户端地址结构
    struct sockaddr_in client_addr;
    memset(&srv_addr,0,sizeof(struct sockaddr_in));
    socklen_t len = sizeof(client_addr);
    printf("服务器开始监听...\n");

    char recv_buf[1024]; //接收缓冲区
    char send_buf[1024];   //发送缓冲区
    while(1){

        //连接成功返回一个通信套接字
        int fd = accept(lfd,(struct sockaddr*)&client_addr,&len);
        if(fd == -1){
                printf("accept() failed.\n");
                continue;
        }
         //char* inet_ntoa(struct in_addr in); 将结构体in_addr作为参数,返回点分十进制的IP地址
        printf("客户端已连接( %s )\n",inet_ntoa(client_addr.sin_addr));
        
        //发送消息给客户端,告诉客户端可以发文件头部信息了
        memset(send_buf,0,sizeof(send_buf));
        strcpy(send_buf,"请输入要传输文件的完整路径: "); 
        send(fd,send_buf,sizeof(send_buf),0);

        //接收文件名和文件大小(头部信息)
        memset(recv_buf,0,sizeof(recv_buf));
        recv(fd,recv_buf,sizeof(recv_buf),0);

        struct st_fileinfo fileinfo;
        memset(&fileinfo,0,sizeof(fileinfo));
        memcpy(&fileinfo,recv_buf,sizeof(fileinfo)); //接收到的数据转换成结构体
        printf("接收的结构体:(%s:%d)\n",fileinfo.filename,fileinfo.filesize);

        //发送消息给客户端,告诉客户端可以发文件内容了
        memset(send_buf,0,sizeof(send_buf));
        strcpy(send_buf,"ok"); 
        send(fd,send_buf,sizeof(send_buf),0);   

        printf("给客户端回复ok应答,开始接收文件...\n");
        //接收文件内容
        string path = "./data/" + string(fileinfo.filename);
        FILE* fp = fopen(path.data(),"w");

         int onread = 0;     //每次接收的字节数
        int totalbytes = 0; //已经接收的总字节数
        char buf[20]={0};   //存放每次接收数据的缓冲区,每次接收20个字节

        while(true){
            //计算本次应该接收的字节数
            if(fileinfo.filesize - totalbytes > 20)
                onread = 20;
            else
                onread = fileinfo.filesize - totalbytes;

            //接收文件内容
            recv(fd,buf,sizeof(buf),0);

            //把接收到的内容写入到文件
            fwrite(buf,sizeof(buf),sizeof(char),fp);
             //printf("接收的数据:%s\n",buf);

            //计算已接收的总字节数,如果接收完毕,跳出循环
            totalbytes += onread;

            if(totalbytes == fileinfo.filesize)
                break;
        }
        printf("接收完毕\n");

        fclose(fp);

    }

    close(lfd);
    return 0;
}